package com.hrg.rds.biz.waybill.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.hrg.rds.biz.comm.NavService;
import com.hrg.rds.biz.config.RdsConfiguration;
import com.hrg.rds.biz.robot.domain.AgvStatus;
import com.hrg.rds.biz.robot.domain.AgvVo;
import com.hrg.rds.biz.traffic.NavType;
import com.hrg.rds.biz.traffic.NavVo;
import com.hrg.rds.biz.waybill.domain.instance.*;
import com.hrg.rds.biz.waybill.domain.template.TemplateParam;
import com.hrg.rds.biz.waybill.domain.template.WaybillTemplateDto;
import com.hrg.rds.biz.waybill.domain.template.WaybillTemplateVo;
import com.hrg.rds.biz.waybill.domain.template.cmd.*;
import com.hrg.rds.biz.waybill.service.AgvMsgService;
import com.hrg.rds.biz.waybill.service.CmdInstanceService;
import com.hrg.rds.infra.executor.ExecutorUtil;
import com.hrg.rds.biz.waybill.mapper.CmdMapper;
import com.hrg.rds.biz.waybill.mapper.WaybillInstanceMapper;
import com.hrg.rds.biz.waybill.service.WaybillTemplateService;
import com.hrg.rds.infra.async.executor.Async;
import com.hrg.rds.infra.async.wrapper.WorkerWrapper;
import com.hrg.seer.sdk.rbk.RbkResult;
import com.hrg.seer.sdk.rbk.RbkResultKind;
import com.hrg.sys.framework.web.domain.AjaxResult;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.hrg.rds.biz.waybill.service.WaybillInstanceService;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

@Slf4j
@Service
public class WaybillInstanceServiceImpl implements WaybillInstanceService {
	@Autowired
	private WaybillInstanceMapper waybillInstanceMapper;
	@Autowired
	private CmdInstanceService cmdInstanceService;
	@Autowired
	private WaybillTemplateService waybillTemplateService;
	@Autowired
	private CmdMapper cmdMapper;
	@Autowired
	private AgvMsgService agvMsgService;

	@Override
	public List<WaybillInstanceVo> selectList(WaybillInstanceDto waybillInstanceDto) {
		List<WaybillInstance> waybillInstances = waybillInstanceMapper.selectList(waybillInstanceDto);
		if (CollUtil.isEmpty(waybillInstances)) {
			return Collections.emptyList();
		}
		List<WaybillInstanceVo> waybillInstanceVoList = new ArrayList<>();
		List<String> waybillInstanceIds = waybillInstances.stream().map(WaybillInstance::getId).toList();
		List<CmdInstance> cmdInstances = cmdInstanceService.selectList(CmdInstanceDto.builder().waybillInstanceIds(waybillInstanceIds).build());
		List<AgvMsg> agvMsgs = agvMsgService.selectList(AgvMsgDto.builder().waybillInstanceIds(waybillInstanceIds).build());
		Map<String, List<AgvMsg>> collect = agvMsgs.stream().collect(Collectors.groupingBy(AgvMsg::getWaybillInstanceId));
		Map<String, List<CmdInstance>> map = cmdInstances.stream().collect(Collectors.groupingBy(CmdInstance::getWaybillInstanceId));
		waybillInstances.forEach(waybillInstance -> {
			waybillInstanceVoList.add(buildWaybillInstanceVo(waybillInstance, map.get(waybillInstance.getId()), collect.get(waybillInstance.getId())));
		});
		return waybillInstanceVoList;
	}

	@Override
	public WaybillInstanceVo selectById(WaybillInstanceDto waybillInstanceDto) {
		WaybillInstance waybillInstance = waybillInstanceMapper.selectById(waybillInstanceDto);
		List<CmdInstance> cmdInstances = cmdInstanceService.selectList(CmdInstanceDto.builder().waybillInstanceId(waybillInstance.getId()).build());
		List<AgvMsg> agvMsgs = agvMsgService.selectList(AgvMsgDto.builder().waybillInstanceId(waybillInstance.getId()).build());
		Map<String, List<CmdInstance>> map = cmdInstances.stream().collect(Collectors.groupingBy(CmdInstance::getWaybillInstanceId));
		return buildWaybillInstanceVo(waybillInstance, map.get(waybillInstance.getId()), agvMsgs);
	}

	@Transactional(rollbackFor = Exception.class)
	@Override
	public void updateById(WaybillInstance waybillInstance) {
		waybillInstanceMapper.updateById(waybillInstance);
	}

	private CmdInstanceVo buildCmdInstanceVoTree(List<CmdInstance> cmdInstances, WaybillInstanceVo waybillInstanceVo, List<AgvMsg> agvMsgs) {
		List<String> cmdIds = cmdInstances.stream().map(CmdInstance::getCmdId).collect(Collectors.toList());
		List<Cmd> cmds = cmdMapper.selectList(CmdDto.builder().ids(cmdIds).build());
		Map<String, Cmd> cmdMap = cmds.stream().collect(Collectors.toMap(Cmd::getId, Function.identity()));
		List<CmdInstanceVo> cmdInstanceVos = new ArrayList<>();
		Map<String, List<AgvMsg>> map = new HashMap<>();
		if (CollUtil.isNotEmpty(agvMsgs)) {
			map = agvMsgs.stream().collect(Collectors.groupingBy(AgvMsg::getCmdInstanceId));
		}
		Map<String, List<AgvMsg>> finalMap = map;
		cmdInstances.forEach(cmdInstance -> {
			CmdInstanceVo cmdInstanceVo = new CmdInstanceVo();
			cmdInstanceVo.setId(cmdInstance.getId());
			cmdInstanceVo.setParentId(cmdInstance.getParentId());
			cmdInstanceVo.setChildren(new ArrayList<>());
			// 可以没有cmd_id，即动态插入的
			cmdInstanceVo.setCmd(cmdMap.get(cmdInstance.getCmdId()));
			cmdInstanceVo.setWaybillInstanceId(cmdInstance.getWaybillInstanceId());
			cmdInstanceVo.setCmdType(cmdInstance.getCmdType());
			cmdInstanceVo.setOrder(cmdInstance.getOrder());
			cmdInstanceVo.setInputParams(JSONUtil.toList(cmdInstance.getInputParams(), CmdParam.class));
			List<CmdParam> cmdParamList = JSONUtil.toList(cmdInstance.getOutputParams(), CmdParam.class);
			cmdInstanceVo.setOutputParams(cmdParamList);
			// 把所有执行器的输出，添加到执行器输出map中
			cmdParamList.forEach(cmdParam -> waybillInstanceVo.getExecutorOutputParams().put(cmdParam.getKey(), cmdParam));
			cmdInstanceVo.setStartTime(cmdInstance.getStartTime());
			cmdInstanceVo.setEndTime(cmdInstance.getEndTime());
			cmdInstanceVo.setCode(cmdInstance.getCode());
			cmdInstanceVo.setMsg(cmdInstance.getMsg());
			if (finalMap.containsKey(cmdInstance.getId())) {
				cmdInstanceVo.setAgvMsgs(finalMap.get(cmdInstance.getId()).stream().sorted(Comparator.comparing(AgvMsg::getId).reversed()).toList());
			}
			cmdInstanceVos.add(cmdInstanceVo);
		});
		CmdInstanceVo rootCmdInstanceVo = null;
		Map<String, CmdInstanceVo> cmdInstanceVoMap = cmdInstanceVos.stream().collect(Collectors.toMap(CmdInstanceVo::getId, Function.identity()));
		for (CmdInstanceVo cmdInstanceVo : cmdInstanceVos) {
			if (StrUtil.isNotBlank(cmdInstanceVo.getParentId())) {
				CmdInstanceVo parentCmdInstanceVo = cmdInstanceVoMap.get(cmdInstanceVo.getParentId());
				parentCmdInstanceVo.getChildren().add(cmdInstanceVo);
			} else {
				rootCmdInstanceVo = cmdInstanceVo;
			}
		}
		cmdInstanceVoMap.values().forEach(cmdInstanceVo -> cmdInstanceVo.getChildren().sort(Comparator.comparingInt(CmdInstanceVo::getOrder)));
		return rootCmdInstanceVo;
	}

	@Transactional(rollbackFor = Exception.class)
	@Override
	public boolean pause(String waybillInstanceId) {
		Optional<AgvVo> first = RdsConfiguration.AgvIpMap.values().stream().filter(agv -> waybillInstanceId.equals(agv.getWaybillInstanceId())).findFirst();
		if (first.isPresent()) {
			AgvVo agvVo = first.get();
			// 执行中-暂停
			WaybillInstanceStatus waybillInstanceStatus = agvVo.getWaybillInstanceVo().getWaybillInstanceStatus();
			if (waybillInstanceStatus.equals(WaybillInstanceStatus.Executing)) {
				log.error("运单实例不是执行中，waybillInstanceId = {}, agvIp = {}, waybillInstanceStatus = {}", waybillInstanceId, agvVo.getIp(), waybillInstanceStatus);
				return false;
			}
			boolean result  = NavService.taskPause(agvVo.getIp());
			if (result) {
				// 修改数据库
				WaybillInstance waybillInstance = new WaybillInstance();
				waybillInstance.setId(waybillInstanceId);
				waybillInstance.setWaybillInstanceStatus(WaybillInstanceStatus.Pausing);
				//waybillInstance.setUpdateBy(SecurityUtils.getUsername());
				waybillInstance.setUpdateTime(new Date());
				waybillInstanceMapper.updateById(waybillInstance);
				// 修改运单为暂停中
				agvVo.getWaybillInstanceVo().setWaybillInstanceStatus(WaybillInstanceStatus.Pausing);
			} else {
				log.error("运单实例AGV暂停导航失败，waybillInstanceId = {}, agvIp = {}", waybillInstanceId, agvVo.getIp());
			}
		}
		log.error("运单实例AGV没找到，waybillInstanceId = {}", waybillInstanceId);
		return false;
	}

	@Transactional(rollbackFor = Exception.class)
	@Override
	public boolean resume(String waybillInstanceId) {
		Optional<AgvVo> first = RdsConfiguration.AgvIpMap.values().stream().filter(agv -> waybillInstanceId.equals(agv.getWaybillInstanceId())).findFirst();
		if (first.isPresent()) {
			AgvVo agvVo = first.get();
			// 暂停-恢复
			WaybillInstanceStatus waybillInstanceStatus = agvVo.getWaybillInstanceVo().getWaybillInstanceStatus();
			if (waybillInstanceStatus.equals(WaybillInstanceStatus.Pausing)) {
				log.error("运单实例不是暂停中，waybillInstanceId = {}, agvIp = {}, waybillInstanceStatus = {}", waybillInstanceId, agvVo.getIp(), waybillInstanceStatus);
				return false;
			}
			boolean result = NavService.taskResume(agvVo.getIp());
			if (result) {
				// 修改数据库
				WaybillInstance waybillInstance = new WaybillInstance();
				waybillInstance.setId(waybillInstanceId);
				waybillInstance.setWaybillInstanceStatus(WaybillInstanceStatus.Executing);
				//waybillInstance.setUpdateBy(SecurityUtils.getUsername());
				waybillInstance.setUpdateTime(new Date());
				waybillInstanceMapper.updateById(waybillInstance);
				// 修改运单为执行中
				agvVo.getWaybillInstanceVo().setWaybillInstanceStatus(WaybillInstanceStatus.Executing);
			} else {
				log.error("运单实例AGV恢复导航失败，waybillInstanceId = {}, agvIp = {}", waybillInstanceId, agvVo.getIp());
			}
			return result;
		}
		log.error("运单实例AGV没找到，waybillInstanceId = {}", waybillInstanceId);
		return false;
	}

	@Transactional(rollbackFor = Exception.class)
	@Override
	public boolean terminate(String waybillInstanceId) {
		// 修改运单终止，取消导航
		Optional<AgvVo> first = RdsConfiguration.AgvIpMap.values().stream().filter(agv -> waybillInstanceId.equals(agv.getWaybillInstanceId())).findFirst();
		if (first.isPresent()) {
			AgvVo agvVo = first.get();
			boolean result = NavService.taskCancel(agvVo.getIp());
			if (result) {
				// 修改数据库
				WaybillInstance waybillInstance = new WaybillInstance();
				waybillInstance.setId(waybillInstanceId);
				waybillInstance.setWaybillInstanceStatus(WaybillInstanceStatus.Terminated);
				//waybillInstance.setUpdateBy(SecurityUtils.getUsername());
				waybillInstance.setUpdateTime(new Date());
				waybillInstanceMapper.updateById(waybillInstance);
				// 运单终态，修改车辆为不接单
				agvVo.onWaybillInstanceFinished();
				agvVo.setStatus(AgvStatus.BJD_Y);
			}
			return result;
		}
		return false;
	}

	@Transactional(rollbackFor = Exception.class)
	@Override
	public AjaxResult fireInstance(String waybillTemplateId, List<TemplateParam> params, String externalId) {

		// 根据运单模板生成运单实例
		WaybillTemplateDto waybillTemplateDto = new WaybillTemplateDto();
		waybillTemplateDto.setId(waybillTemplateId);
		WaybillTemplateVo waybillTemplateVo = waybillTemplateService.selectList(waybillTemplateDto).getFirst();

		Map<String, TemplateParam> paramMap = params.stream().collect(Collectors.toMap(TemplateParam::getName, Function.identity()));
		//处理参数,参数为基本类型，需要校验必填参数是否为空，表达式参数纯替换暂不支持计算
		Map<String, TemplateParam> map = new HashMap<>();
		for (TemplateParam templateParam : waybillTemplateVo.getInputParams()) {
			TemplateParam actualParam = paramMap.get(templateParam.getName());
			if (templateParam.isRequired() && (actualParam == null || StrUtil.isBlank(actualParam.getValue()))) {
				return AjaxResult.error("必填参数为空");
			}
			templateParam.setValue(actualParam.getValue());
			map.put(templateParam.getName(), templateParam);
		}

		// 创建运单实例
		WaybillInstance waybillInstance = new WaybillInstance();
		waybillInstance.setWaybillTemplateId(waybillTemplateId);
		waybillInstance.setId(IdUtil.getSnowflakeNextIdStr());
		waybillInstance.setExternalId(externalId);
		waybillInstance.setGiveWayPriority(waybillTemplateVo.getGiveWayPriority());
		waybillInstance.setWaybillInstanceStatus(WaybillInstanceStatus.ToBeDispatched);
		Date now = new Date();
		//waybillInstance.setCreateBy(SecurityUtils.getUsername());
		waybillInstance.setCreateTime(now);
		//waybillInstance.setUpdateBy(SecurityUtils.getUsername());
		waybillInstance.setUpdateTime(now);
		// 运单实例参数
		waybillInstance.setInputParams(JSONUtil.toJsonStr(params));
		waybillInstance.setOutputParams(JSONUtil.toJsonStr(Collections.EMPTY_LIST));

		// 根据运单实例和命令树构建命令执行日志
		CmdVo rootCmdVo = waybillTemplateVo.getRootCmdVo();
		// 把实例的输入参数（已知的），先实例化到命令日志中
		List<CmdInstance> cmdInstanceList = new ArrayList<>();
		buildCmdInstanceList(null, rootCmdVo, waybillInstance.getId(), map, now, cmdInstanceList);
		cmdInstanceService.insertBatch(cmdInstanceList);
		CmdInstance first = cmdInstanceList.getFirst();
		waybillInstance.setRootCmdInstanceId(first.getCmdId());
		waybillInstanceMapper.insert(waybillInstance);

		// 把执行上下文（重启时也需要，重试时也需要，终止时退出-没有下一个步骤了），提交到异步线程执行调度中
		WaybillInstanceVo waybillInstanceVo = buildWaybillInstanceVo(waybillInstance, cmdInstanceList, null);
		WorkerWrapper<?,?>[] workerWrapper = ExecutorUtil.wrapWorker(waybillInstanceVo);
		log.debug(JSONUtil.toJsonStr(workerWrapper));
		Async.beginWorkAsync(workerWrapper);
		return AjaxResult.success();
	}


	private WaybillInstanceVo buildWaybillInstanceVo(WaybillInstance waybillInstance, List<CmdInstance> cmdInstances, List<AgvMsg> agvMsgs) {
		WaybillInstanceVo waybillInstanceVo = new WaybillInstanceVo();
		waybillInstanceVo.setId(waybillInstance.getId());
		waybillInstanceVo.setWaybillTemplateId(waybillInstance.getWaybillTemplateId());
		waybillInstanceVo.setExternalId(waybillInstance.getExternalId());
		waybillInstanceVo.setAgvId(waybillInstance.getAgvId());
		waybillInstanceVo.setAgvGroupId(waybillInstance.getAgvGroupId());
		waybillInstanceVo.setGiveWayPriority(waybillInstance.getGiveWayPriority());
		waybillInstanceVo.setWaybillInstanceStatus(waybillInstance.getWaybillInstanceStatus());
		waybillInstanceVo.setStartTime(waybillInstance.getStartTime());
		waybillInstanceVo.setEndTime(waybillInstance.getEndTime());
		waybillInstanceVo.setInputParams(JSONUtil.toList(waybillInstance.getInputParams(), TemplateParam.class));
		waybillInstanceVo.setOutputParams(JSONUtil.toList(waybillInstance.getOutputParams(), TemplateParam.class));
		CmdInstanceVo rootCmdInstanceVo = buildCmdInstanceVoTree(cmdInstances, waybillInstanceVo, agvMsgs);
		List<NavVo> navVos = new ArrayList<>();
		buildNavVos(rootCmdInstanceVo, navVos);
		waybillInstanceVo.setNavVos(navVos);
		waybillInstanceVo.setRootCmdInstanceVo(rootCmdInstanceVo);
		return waybillInstanceVo;
	}

	private void buildNavVos(CmdInstanceVo rootCmdInstanceVo, List<NavVo> navVos) {
		List<CmdInstanceVo> children = rootCmdInstanceVo.getChildren();
		if (CollUtil.isNotEmpty(children)) {
			for (CmdInstanceVo child : children) {
				buildNavVos(child, navVos);
			}
		} else {
			if (rootCmdInstanceVo.getCmdType().equals(CmdType.AgvNavOperationCmd)) {
				Map<String, CmdParam> map = rootCmdInstanceVo.getInputParams().stream().collect(Collectors.toMap(CmdParam::getKey, Function.identity()));
				NavVo navVo = new NavVo();
				navVo.setStation(map.get("targetStation").getEvaluatedValue());
				if (map.get("operation").getEvaluatedValue().toLowerCase().startsWith("load")) {
					// 以load开头
					navVo.setNavType(NavType.Load);
				} else if (map.get("operation").getEvaluatedValue().toLowerCase().startsWith("unload")) {
					// 以unload开头
					navVo.setNavType(NavType.Unload);
				}
				navVos.add(navVo);
			}
			if (rootCmdInstanceVo.getCmdType().equals(CmdType.AgvNavCmd)) {
				Map<String, CmdParam> map = rootCmdInstanceVo.getInputParams().stream().collect(Collectors.toMap(CmdParam::getKey, Function.identity()));
				NavVo navVo = new NavVo();
				navVo.setStation(map.get("targetStation").getEvaluatedValue());
				navVo.setNavType(NavType.Normal);
				navVos.add(navVo);
			}
		}
	}
	private void buildCmdInstanceList(String parentCmdInstanceId, CmdVo cmdVo, String waybillInstanceId, Map<String, TemplateParam> inputParams, Date now, List<CmdInstance> cmdInstanceList) {
		CmdInstance cmdInstance = new CmdInstance();
		cmdInstance.setId(IdUtil.getSnowflakeNextIdStr());
		cmdInstance.setParentId(parentCmdInstanceId);
		cmdInstance.setCmdId(cmdVo.getId());
		cmdInstance.setWaybillInstanceId(waybillInstanceId);
		cmdInstance.setCmdType(cmdVo.getCmdType());
		cmdInstance.setOrder(cmdVo.getOrder());
		List<CmdParam> cmdParamList = cmdVo.getInputParams();
		cmdParamList.forEach(cmdParam -> {
			if (cmdParam.getCmdParamType().equals(CmdParamType.Expression) && cmdParam.isRequired() && inputParams.containsKey(cmdParam.getKey())) {
				cmdParam.setEvaluatedValue(inputParams.get(cmdParam.getKey()).evaluateValue());
			}
		});
		cmdInstance.setInputParams(JSONUtil.toJsonStr(cmdParamList));
		cmdInstance.setOutputParams(JSONUtil.toJsonStr(Collections.EMPTY_LIST));
		//cmdLog.setCreateBy(SecurityUtils.getUsername());
		cmdInstance.setCreateTime(now);
		//cmdLog.setUpdateBy(SecurityUtils.getUsername());
		cmdInstance.setUpdateTime(now);
		cmdInstanceList.add(cmdInstance);

		List<CmdVo> children = cmdVo.getChildren();
		if (CollUtil.isNotEmpty(children)) {
			children.forEach(child -> {
				buildCmdInstanceList(cmdInstance.getId(), child, waybillInstanceId, inputParams, now, cmdInstanceList);
			});
		}
	}


}
